<?php
/**
 * DB
 * @filesource
 */
/**
 * Namespace
 */
namespace framework\library\database;
/**
 * Dependencies
 */
use framework\library\database\Query_Builder;
use \PDO;
/**
 * DB Class
 *
 * Plurious Database
 *
 *  Generic PDO Abstraction Class
 * 
 *  NOTE: There shoul be no SQL in this file!
 * 
 * @author Randy Casburn <randy@plurious.com>
 * @copyright Copyright Plurious Software, all rights reserved
 * @license MIT
 * @license http://www.opensource.org/licenses/mit-license.html
 * @link http://plurious.com/framework/documentation/classes/Front.html
 * @version 2.1
 * 
 * @uses framework\library\database\Query_Builder
 * @uses \PDO
 */
class DB extends Query_Builder {

    /** Holds static instannce reference for Singleton */
    private static $dbRef;

    /** Holds PDO Object */
    public $db;

    /** Indicates Number of active statement objects */
    private $statement_counter = 0;

    /**
     * Returns Singleton Instance
     * 
     * @param string $type
     * @param string $host
     * @param int $port
     * @param string $db_name
     * @param string $user
     * @param string $password
     * @param array $params
     * @return Instance Mysql_DB Object
     */
    public static function getInstance($type, $host, $db_name, $user, $password, $params = array(), $port = 3306) {
        if (null === self::$dbRef) {
            self::$dbRef = new self($type, $host, $db_name, $user, $password, $params = array(), $port = 3306);
        }
        return self::$dbRef;
    }

    /**
     * A Private Constructor;
     * 
     * Constructs PDO Abstraction object.
     * 
     * @param string $type
     * @param string $host
     * @param int $port
     * @param string $db_name
     * @param string $user
     * @param string $password
     * @param array $params
     * @return Object PDO Object
     */
    private function __construct($type, $host, $db_name, $user, $password, $port = 3306) {
        switch (strtoupper($type)) {
            case 'MSSQL' :
                $dsn = "mssql:host=$host;dbname=$db_name";
                break;
            case 'POSTGRE' :
                $postgre_port = "5432";
                $dsn = "pgsql:host=$host;port=$postgre_port;dbname=$db_name;user=$user;password=$password";
                break;
            case 'SQLITE' :
                if(is_file($db_name)){
                   $dsn = "sqlite:".DB_DIR."$db_name";
                } else {
                    $dsn = "";
                }
                break;
            default : // MYSQL
                $params = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8", PDO::ATTR_PERSISTENT => true);
                $dsn = "mysql:host=$host;dbname=$db_name";
                break;
        }
        try {
            $this->db = new PDO($dsn, $user, $password, $params);
            $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            $this->db = null;
            if (!DEBUG) {
                file_put_contents('pdo_errors.txt', $e->getMessage(), FILE_APPEND); // log the errors     
                die($this->_error_message);
            } else {
                die($e->getMessage());
            }
        }
        return $this->db;
    }

    /**
     * Closes the DB Connection upon Object removal
     */
    public function __destruct() {
        $this->db = null;
    }

    /**
     * Uses PDO to prepare SQL statements, close stale cursors & 
     * execute PDO statements
     * 
     * Logs error to pdo_errors.txt
     * 
     * @param string $sql
     * @param array $values
     * @return PDO::Statement 
     */
    protected function exec($sql, $values) {
        $statement_name = 'statement' . $this->statement_counter;
        $this->statement_counter++;
        try {
            ${$statement_name} = $this->db->prepare($sql);
            if ($this->statement_counter != 1) {
                ${$statement_name}->closeCursor();
                $this->statement_counter--;
            }
            $result = ${$statement_name}->execute($values);
        } catch (PDOException $e) {
            if (!DEBUG) {
                file_put_contents('pdo_errors.txt', "Query Error: " . $e->getMessage() . "\nSQL: " . $sql, FILE_APPEND);
            } else {
                echo "Query Error: " . $e->getMessage() . '<br>';
                echo "SQL : " . $sql;
                exit;
            }
        }

        return ${$statement_name};
    }

    public function query($sql, Array $values){
        $statement = $this->exec($sql, $values);
        if(stristr($sql,'SELECT')){
            $result = $statement->fetchAll(PDO::FETCH_ASSOC);
        } else {
            $result = $statement->rowCount();
        }
        return $result;
    }
    
    
    /**
     * Creates a string of comma separated arrray keys.
     * 
     * Used to create the field listing for SQL statements (upates, inserts).
     * 
     * @param array $data
     * @return string of comma separated array keys 
     */
    public function getColumns($data) {
        return implode(", ", array_keys($data));
    }

    /**
     * Creates string of arrays keys prepended with ':' to be used as
     * PDO query parameters
     * 
     * @param array $data
     * @return string PDO query parameters 
     */
    public function getQueryParams($data) {
        $data = array_keys($data);
        return implode(',', array_map(function($name) {
                                    return "':" . $name . "'";
                                }, array_values($data)));
    }

    /**
     * Retrieves the last id inserted into a table.
     * 
     * @return int id of most recent inserted record
     */
    public function lastInsertId() {
        return $this->db->lastInsertId();
    }
    
   // Override -- No cloning allowed
    public function __clone()
    {
        trigger_error('Clone is not allowed.', E_USER_ERROR);
    }
   // Override -- No serializing/unserializing allowed
    public function __wakeup()
    {
        trigger_error('Unserializing is not allowed.', E_USER_ERROR);
    }

}